package com.aboverock.module.shiro.service;

import com.aboverock.autoconfigure.properties.ShiroProperties;
import com.aboverock.common.utils.JsonMapper;
import com.aboverock.core.shiro.authz.rest.RestFilterChainManager;
import com.aboverock.core.shiro.authz.rest.RestPathMatchingFilterChainResolver;
import com.aboverock.core.shiro.authz.rest.RestShiroFilterFactoryBean;
import com.aboverock.core.shiro.util.ShiroRestUrlUtil;
import com.aboverock.core.spring.SpringContextHolder;
import com.aboverock.module.shiro.config.ShiroConfig;
import com.aboverock.module.system.domain.Resource;
import com.aboverock.module.system.domain.RestUrl;
import com.aboverock.module.system.mapper.ResourceMapper;
import com.fasterxml.jackson.databind.JavaType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * The Class ShiroService.
 *
 * @author Rock Wang
 */
@Component
@EnableConfigurationProperties({ ShiroProperties.class })
public class ShiroService {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ShiroProperties shiroProperties;

    @Autowired
    private ResourceMapper resourceMapper;

    public static final String[] DEFAULT_FILTER_CHAIN_URL = { "/sys/login", "/sys/tokenLogin",
        "/sys/logout", "/system/user/local/isSupport", "/sys/encrypt", "/system/user/current",
        "/system/resource/user" };

    /**
     * 初始化权限.
     *
     * @return the map
     */
    public Map<String, String> loadFilterChainDefinitions() {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // swagger rest api 相关资源路径添加允许shiro匿名访问
        filterChainDefinitionMap.put("/swagger-ui.html/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/v2/api-docs/**", "anon");
        // actuator 应用状态监测api接口
        filterChainDefinitionMap.put("/actuator/**", "anon");

        // rest api接口使用AppKey过滤器
        filterChainDefinitionMap.put("/restapis/**", ShiroConfig.APP_KEY_FILTER);

        filterChainDefinitionMap.put("/sys/login", "anon");
        filterChainDefinitionMap.put("/sys/tokenLogin", "anon");
        filterChainDefinitionMap.put("/sys/logout", "anon");
        filterChainDefinitionMap.put("/system/user/local/isSupport", "anon");
        filterChainDefinitionMap.put("/sys/encrypt", "anon");
        // 获取登录用户信息
        filterChainDefinitionMap.put("/system/user/current", ShiroConfig.JWT_FILTER);
        // 获取登录用户资源
        filterChainDefinitionMap.put("/system/resource/user", ShiroConfig.JWT_FILTER);

        // 自定义加载权限资源关系
        List<Resource> resourceList = resourceMapper.selectList(null);
        List<RestUrl> restUrls = new LinkedList<RestUrl>();
        resourceList.forEach(resource -> {
            if (StringUtils.isNotBlank(resource.getRestUrls())) {
                List<RestUrl> urls = parseRestUrls(resource.getRestUrls());
                urls.forEach(url -> url.setPermissions(resource.getPermissions()));
                restUrls.addAll(urls);
            }
        });

        Map<String, Set<String>> urlPermissions = new HashMap<String, Set<String>>(16);
        for (RestUrl restUrl : restUrls) {
            String url = ShiroRestUrlUtil.buildRestUrl(restUrl.getMethod(),
                    genPattern(restUrl.getUrl()));
            if (null == urlPermissions.get(url)) {
                Set<String> permissions = new HashSet<String>();
                urlPermissions.put(url, permissions);
            }
            urlPermissions.get(url).add(restUrl.getPermissions());
        }

        urlPermissions.forEach((url, perms) -> {
            String permission = "perms[" + StringUtils.join(perms.toArray(), ",") + "]";
            filterChainDefinitionMap.put(url, ShiroConfig.JWT_FILTER + "," + permission);
            logger.debug("Data-Smart：加载权限 {} -> {},{}", url, ShiroConfig.JWT_FILTER, permission);
        });

        filterChainDefinitionMap.putAll(shiroProperties.getFilterUrl());

        if (!shiroProperties.getFilterUrl().containsKey("/**")) {
            filterChainDefinitionMap.put("/**", ShiroConfig.JWT_FILTER);
        }

        return filterChainDefinitionMap;
    }

    /**
     * 重新加载权限
     */
    public void updatePermission() {
        RestShiroFilterFactoryBean shiroFilterFactoryBean = SpringContextHolder
                .getBean(RestShiroFilterFactoryBean.class);

        synchronized (shiroFilterFactoryBean) {
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
            } catch (Exception e) {
                logger.error("Data-Smart: get ShiroFilter from shiroFilterFactoryBean error!");
                throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
            }

            RestPathMatchingFilterChainResolver filterChainResolver = (RestPathMatchingFilterChainResolver) shiroFilter
                    .getFilterChainResolver();
            RestFilterChainManager manager = (RestFilterChainManager) filterChainResolver
                    .getFilterChainManager();

            // 清空老的权限控制
            manager.getFilterChains().clear();

            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitions());
            // 重新构建生成
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim().replace(" ", "");
                manager.createChain(url, chainDefinition);
            }

            logger.info("Data-Smart: 更新权限成功");
        }
    }

    private List<RestUrl> parseRestUrls(String urlsJson) {
        JsonMapper jsonMapper = JsonMapper.getInstance();
        JavaType javaType = jsonMapper.createCollectionType(ArrayList.class, RestUrl.class);
        return jsonMapper.fromJson(urlsJson, javaType);
    }

    private String genPattern(String url) {
        return url.replaceAll("\\{\\w+\\}", "*");
    }
}
